
package cn.zlg.common.datadump.service;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.zlg.common.datadump.annotation.DataDumpEntity;
import cn.zlg.common.datadump.bean.BatchDeleteResult;
import cn.zlg.common.datadump.bean.DataDumpDef;
import cn.zlg.common.datadump.bean.ImportResult;
import cn.zlg.common.datadump.condition.HttpServletRequestQueryConditionBuilder;
import cn.zlg.common.datadump.condition.QueryCondtionWrapper;
import cn.zlg.common.datadump.enums.ImportDuplicateProcess;
import cn.zlg.common.datadump.executor.JPAQueryExportExecutor;
import cn.zlg.common.datadump.executor.JPAQueryImportExecutor;
import com.joysuch.wwyt.bp.enums.DataStateTypes;
import com.joysuch.wwyt.dataperm.context.DataPermContextHolder;
import com.joysuch.wwyt.dynamicdata.DynamicImportData;
import com.joysuch.wwyt.dynamicdata.DynamicReadData;
import com.joysuch.wwyt.dynamicdata.entity.BaseDynamicFieldConfig;
import com.joysuch.wwyt.dynamicdata.service.BaseDynamicFieldConfigService;
import com.joysuch.wwyt.risk.entity.dsl.QRiskPoint;
import com.joysuch.wwyt.risk.enums.RiskPointStatus;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

@Component
public class DataDumpService {

    private static final Logger logger = LoggerFactory.getLogger(DataDumpService.class);

    @Autowired
    private DataDumpDefRegistry dataDumpDefRegistry;

    @Autowired
    private JPAQueryExportExecutor exportExecutor;

    @Autowired
    private BatchDeleteService batchDeleteExecutor;

    @Autowired
    private JPAQueryImportExecutor importExecutor;

    @Autowired
    private BaseDynamicFieldConfigService fieldConfigService;

    public void validate(Class defClass) throws Exception {
        DataDumpEntity annotation2 = (DataDumpEntity) defClass.getAnnotation(DataDumpEntity.class);
        Assert.notNull(annotation2, "类" + defClass.getName() + "没有@DataDumpEntity注解");
        exportExecutor.findFieldDescriptors(defClass, annotation2);
    }

    public void register(Class defClass, DataDumpEntity annotation)
            throws InstantiationException, IllegalAccessException {
        dataDumpDefRegistry.register(defClass, annotation);
    }

    @DynamicReadData(selector = "export")
    public List doExport(String name, HttpServletRequest request) throws Exception {
        try {
            // 启用新的sql join 链接规则
            DataPermContextHolder.setTenantAndOrgCodeJoinSwitchFlag(true);
            DataDumpDef dumpDef = dataDumpDefRegistry.getDefinitionByHandler(name);
            Assert.notNull(dumpDef, "没有名为" + name + "的导出处理器，请检查");
            QueryCondtionWrapper queryCondition = null;
            if (dumpDef != null) {
                queryCondition = HttpServletRequestQueryConditionBuilder.build(request, dumpDef.queryCondtionDef);
            }
            //风险点做特殊处理 待优化
            if (name.equals("risk-point-dump")) {
                QRiskPoint point = QRiskPoint.riskPoint;
                Predicate p = null;
                p = ExpressionUtils.and(p, ExpressionUtils.or(point.state.notIn(DataStateTypes.DELETE.getState()),
                        point.state.isNull()));
                p = ExpressionUtils.and(p, ExpressionUtils.or(point.shared.isNull(), point.shared.eq(0)));
                p = ExpressionUtils.and(p, point.status.eq(RiskPointStatus.AUDIT_SUCCESS.getIndex()));
                queryCondition.setPredicate(p);
            }
            return exportExecutor.query(dumpDef.defClass, queryCondition);
        } finally {
            // 清理新的sql join 链接规则
            DataPermContextHolder.clearTenantAndOrgCodeJoinSwitchFlag();
        }
    }

    @DynamicImportData
    public ImportResult doImport(String name, MultipartFile file, int sheetIndex, int titleRows, int headRows, ImportDuplicateProcess duplicateProcess, HttpServletRequest request) throws Exception {
        DataDumpDef dumpDef = dataDumpDefRegistry.getDefinitionByHandler(name);
        Assert.notNull(dumpDef, "没有名为" + name + "的导出处理器，请检查");
        List rows = parseData(file, sheetIndex, titleRows, headRows, dumpDef.defClass, true);
        if (rows.size() > 0) {
            // 从底部遍历，删除全部是空属性的数据
            removeAllEmptyRowsFromTail(rows);
        }

        System.err.println(rows);
        return importExecutor.doImport(dumpDef.defClass, rows, titleRows + headRows + 1, duplicateProcess);
    }

    private void removeAllEmptyRowsFromTail(List rows) throws InvocationTargetException, IllegalAccessException {
        boolean stop = false;
        do {
            Object obj = rows.get(rows.size() - 1);
            boolean allEmpty = isObjectAllEmpty(obj);
            stop = !allEmpty;
            System.err.println(allEmpty + " -> " + obj);
            if (allEmpty) {
                rows.remove(rows.size() - 1);
            }
        } while (!stop && rows.size() > 0);
    }

    private boolean isObjectAllEmpty(Object obj) throws InvocationTargetException, IllegalAccessException {
        PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(obj.getClass());
        boolean hasNonPrimitiveProperties = false;
        System.err.println(descriptors.length);
        for (PropertyDescriptor pd : descriptors) {
            if ("class".equals(pd.getName()) || pd.getWriteMethod() == null) {
                // Object 自带
                continue;
            }
            if (!pd.getPropertyType().isPrimitive()) {
                hasNonPrimitiveProperties = true;
                Object value = pd.getReadMethod().invoke(obj);
//                System.err.println(pd.getName() + " -> " + value + " -> " + (value == null));
                if (value != null) {
                    return false;
                }
            }
        }
        // 存在非primitive属性，且全为null
        return hasNonPrimitiveProperties;
    }

    private <T> List<T> parseData(MultipartFile file, int sheetIndex, int titleRows, int headRows, Class<T> clazz,
                                  boolean trimStringData) {
        ImportParams params = new ImportParams();
        try {
            params.setStartSheetIndex(sheetIndex);
            params.setHeadRows(headRows);
            params.setTitleRows(titleRows);
            List<T> result = ExcelImportUtil.importExcel(file.getInputStream(), clazz, params);
            if (trimStringData) {
                for (T b : result) {
                    trimData(b);
                }
            }
            return result;
        } catch (Exception e) {
            logger.error("parse data exception:", e);
        }
        return null;
    }

    private void trimData(Object rb) {
        PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(rb.getClass());
        for (PropertyDescriptor pd : descriptors) {
            if (pd.getPropertyType() == String.class && pd.getWriteMethod() != null && pd.getReadMethod() != null) {
                try {
                    String value = (String) pd.getReadMethod().invoke(rb, null);
                    if (value != null) {
                        value = value.trim();
                        pd.getWriteMethod().invoke(rb, value);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public BatchDeleteResult batchDelete(String name, HttpServletRequest request) throws Exception {
        DataDumpDef dumpDef = dataDumpDefRegistry.getDefinitionByHandler(name);
        Assert.notNull(dumpDef, "没有名为" + name + "的批量删除处理器，请检查");
        List<Long> ids = HttpServletRequestQueryConditionBuilder.extractParameterValuesAndConvert(request, "ids", Long.class);
        if (ids.isEmpty()) {
            return BatchDeleteResult.empty();
        }

        return batchDeleteExecutor.batchDelete(dumpDef.defClass, ids);
    }

    public Workbook createDynamicExcel(List list, String handler, String dynamicDataBizType) {
        List<ExcelExportEntity> entityList = new ArrayList<>();
        ExcelExportEntity excelExportEntity;
        Class entity = getEntityClass(handler);
        Field[] fields = entity.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            Excel annotation = field.getAnnotation(Excel.class);
            if (annotation != null) {
                String clownName = annotation.name();
                excelExportEntity = new ExcelExportEntity(clownName, fieldName);
                entityList.add(excelExportEntity);
            }
        }
        List<BaseDynamicFieldConfig> configList = fieldConfigService.findByBizType(dynamicDataBizType);
        for (BaseDynamicFieldConfig config : configList) {
            excelExportEntity = new ExcelExportEntity(config.getFieldDesc(), config.getFieldName());
            entityList.add(excelExportEntity);
        }
        return ExcelExportUtil.exportExcel(new ExportParams(null, "数据"), entityList, list);
    }

    public String getBusinessName(String handler) {
        DataDumpDef dumpDef = dataDumpDefRegistry.getDefinitionByHandler(handler);
        return dumpDef == null ? null : dumpDef.annotation.businessName();
    }

    public String getDynamicDataBizType(String handler) {
        DataDumpDef dumpDef = dataDumpDefRegistry.getDefinitionByHandler(handler);
        return dumpDef == null ? null : dumpDef.annotation.dynamicDataBizType();
    }

    public String getDynamicDataBizCode(String handler) {
        DataDumpDef dumpDef = dataDumpDefRegistry.getDefinitionByHandler(handler);
        return dumpDef == null ? null : dumpDef.annotation.dynamicDataBizCode();
    }

    public DataDumpDef getDataDumpDef(String handler) {
        return dataDumpDefRegistry.getDefinitionByHandler(handler);
    }

    public Class getEntityClass(String handler) {
        DataDumpDef dumpDef = dataDumpDefRegistry.getDefinitionByHandler(handler);
        return dumpDef == null ? null : dumpDef.defClass;
    }

}
